#include <cstdio>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string>
#include <pthread.h>
#include <map>
#include <list>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>


#define MAX_SIZE 1024
#define LOGINOK 00
#define LOGINNOK 01
#define MESSAGESENDOK 02
#define MESSAGESAVEDFORFUTURE 03
#define MESSAGEDROPPED 04
#define CODESIZE 2


using namespace std;

map<string, list<string> > waiingMessages;
vector<string> knownUsers;
map<string, FILE> loggedUsers;

string intToHex(int i) {
	stringstream stream;
	stream << std::setfill('0') << setw(sizeof(int) * 2) << std::hex << i;
	return stream.str();
}

int hexToInt(string i) {

	int out;

	stringstream stream;

	stream << std::hex << i;
	stream >> out;

	cout << out << flush;

	return out;
}


void sendMessage(int code, string msg, FILE *fp) {
	stringstream message;

	message << code << msg;

	string msg1 = message.str();
	string length = intToHex(msg1.size());

	string finalMessage = length + msg1;

	cout << "Sending message: " << finalMessage << endl << flush;

	fputs(finalMessage.c_str(), fp);

}


void *oneClient(void *arg) {
	string username;
	FILE *fp = (FILE*) arg;
	while (true) {

		//nacteme velikost zpravy
		char sizeBuffer[9];
		fgets(sizeBuffer, sizeof(sizeBuffer), fp);
		int size = hexToInt(sizeBuffer);

		//nacteme kod zpravy - 01, 02, 03...
		char codeBuffer[CODESIZE+1];
		fgets(codeBuffer, sizeof(codeBuffer), fp);
		int messageCode = atoi(codeBuffer);

		//----------------------------- user login ------------------------------
		if (messageCode == 0) {

			//precteme jmeno
			char messageBuffer[(size - CODESIZE) + 1];
			fgets(messageBuffer, sizeof(messageBuffer), fp);
			string name = messageBuffer;
			username = name;

			cout << "Login : " << name << endl << flush;

			if (loggedUsers.count(name) == 0) {
				//pokud uzivatel jeste neni prihlasen, tak ho pridame
				loggedUsers[name] = *fp;
			} else {
				//pokud uz prihlaseny je, vratime chybu a zabijem vlakno
				sendMessage(05, "This user is already logged in! Get OUT!", fp);
				fclose(fp);
				return 0;
			}


			//zname toho uzivatele?
			if (find(knownUsers.begin(), knownUsers.end(), name) == knownUsers.end()) {
				//pridame uzivatele do znamych uzivatelu
				knownUsers.push_back(name);
			}
		}
		//-----------------------------------------------------------

		//----------------------------- user logout ------------------------------
		if (messageCode == 1) {
			loggedUsers.erase(username);
			fclose(fp);
			return 0;

		}
		//-----------------------------------------------------------





	}

	fclose(fp);
	return 0;
}

int main()

{

	int sock_descriptor, conn_desc;

	struct sockaddr_in serv_addr, client_addr;

	char buff[MAX_SIZE];

	//pouzijem TCP
	sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);

	if (sock_descriptor < 0)
		printf("Failed creating socket\n");

	bzero((char *) &serv_addr, sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;

	//poslouchame na vsech adresach
	serv_addr.sin_addr.s_addr = INADDR_ANY;

	//port
	serv_addr.sin_port = htons(1111);

	//prirazeni portu
	if (bind(sock_descriptor, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
			< 0)
		printf("Failed to bind\n");

	//max pet cekajicich spojeni
	listen(sock_descriptor, 5);

	while (true) {
		printf("Waiting for connection...\n");

		int size = sizeof(client_addr);

		pthread_t thread;
		FILE *file;
		int con;

		con = accept(sock_descriptor, (struct sockaddr *) &client_addr, &size);
		;
		file = fdopen(con, "r+");

		pthread_create(&thread, 0, oneClient, file);
		pthread_detach(thread);

	} //endWhile

	close(sock_descriptor);

	return 0;

}

